package com.tvunetworks.center.device.util;

import org.apache.commons.lang.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.regex.Pattern;

/**
 * @program: tvucc-aggregator
 * @author lebronchen
 * @create: 2020-02-19 12:46
 **/
public class IpUtils {
    /**
     *
     */
    public static final  String  HEADER_USER_AGENT    = "user-agent";
    /**
     *
     */
    public static final Pattern PATTERN              = Pattern.compile("^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$");
    private static final String  X_FORWARDED_FOR      = "x-forwarded-for";
    private static final String  COMMA_DELIMITER      = ",";
    private static final String  UNKNOWN_STR          = "unknown";
    private static final String  PROXY_CLIENT_IP      = "Proxy-Client-IP";
    private static final String  WL_PROXY_CLIENT_IP   = "WL-Proxy-Client-IP";
    private static final char    DOT_DELIMITER        = '.';
    private static final String  X_REAL_IP            = "X-Real-IP";
    private static final String  HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR";
    private static final String  HTTP_CLIENT_IP       = "HTTP_CLIENT_IP";

    /**
     * Gets the client ip addr.
     *
     * @param request
     *            the request
     * @return the client ip addr
     */
    public static String getClientIpAddr(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    /**
     * Gets the real ips.
     *
     * @return the real ips
     */
    public static List<String> getRealIps() {
        List<String> ips = new ArrayList<String>();
        String localip = null;// 本地IP，如果没有配置外网IP则返回它
        String netip = null;// 外网IP
        Enumeration<NetworkInterface> netInterfaces;
        try {
            netInterfaces = NetworkInterface.getNetworkInterfaces();
        } catch (SocketException e) {
            return null;
        }
        InetAddress ip = null;
        boolean finded = false;// 是否找到外网IP
        while (netInterfaces.hasMoreElements() && !finded) {
            NetworkInterface ni = netInterfaces.nextElement();
            Enumeration<InetAddress> address = ni.getInetAddresses();
            while (address.hasMoreElements()) {
                ip = address.nextElement();
                if (!ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {// 外网IP
                    netip = ip.getHostAddress();
                    ips.add(netip);
                    finded = true;
                    break;
                } else if (ip.isSiteLocalAddress() && !ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {// 内网IP
                    localip = ip.getHostAddress();
                    ips.add(localip);
                }
            }
        }
        return ips;
    }


    /**
     * 获取公网IP
     *
     * @param request request
     * @return String
     */
    public static String getClientPublicIP(HttpServletRequest request) {
        String ip = request.getHeader(X_FORWARDED_FOR);
        if (StringUtils.isNotBlank(ip)) {
            String[] ips = ip.trim().split(COMMA_DELIMITER);
            for (String item : ips) {
                if (StringUtils.isNotBlank(item)) {
                    item = item.trim();
                    if (isIPv4Public(item)) {
                        return item;
                    }
                }
            }
        }
        if (isIPv4Public(ip = request.getHeader(PROXY_CLIENT_IP))) {
            return ip.trim();
        }
        if (isIPv4Public(ip = request.getHeader(WL_PROXY_CLIENT_IP))) {
            return ip.trim();
        }
        if (isIPv4Public(ip = request.getHeader(HTTP_CLIENT_IP))) {
            return ip.trim();
        }
        if (isIPv4Public(ip = request.getHeader(HTTP_X_FORWARDED_FOR))) {
            return ip.trim();
        }
        if (isIPv4Public(ip = request.getHeader(X_REAL_IP))) {
            return ip.trim();
        }
        if (isIPv4Public(ip = request.getRemoteAddr())) {
            return ip.trim();
        }
        //logRequest(request, ip);
        return ip;
    }

    /**
     * 是否是有效的公网IP
     *
     * @param ip ip
     * @return boolean
     */
    public static boolean isIPv4Public(String ip) {
        return isIPv4Valid(ip) && !isIPv4Private(ip);
    }

    /**
     *
     * @param ip  ip
     * @return boolean
     */
    public static boolean isIPv4Valid(String ip) {
        return StringUtils.isNotBlank(ip) && PATTERN.matcher(ip.trim()).matches();
    }

    /**
     *
     * @param ip ip
     * @return boolean
     */
    public static boolean isIPv4Private(String ip) {
        if (!StringUtils.isNotBlank(ip)) {
            throw new IllegalArgumentException("the parameter ip is not allowed be empty");
        }

        if (ip.equals("127.0.0.1")) {
            return true;
        }

        ip = ip.trim();
        long longIp = ipV4ToLong(ip);
        return (longIp >= ipV4ToLong("10.0.0.0") && longIp <= ipV4ToLong("10.255.255.255")) ||
                (longIp >= ipV4ToLong("172.16.0.0") && longIp <= ipV4ToLong("172.31.255.255")) ||
                longIp >= ipV4ToLong("192.168.0.0") && longIp <= ipV4ToLong("192.168.255.255");
    }

    /**
     *
     * @param ip ip
     * @return long
     */
    public static long ipV4ToLong(String ip) {
        String[] octets = ip.split("\\.");
        return (Long.parseLong(octets[0]) << 24) + (Integer.parseInt(octets[1]) << 16) +
                (Integer.parseInt(octets[2]) << 8) + Integer.parseInt(octets[3]);
    }



    /**
     * Gets the real ip.
     *
     * @return the real ip
     */
    public static String getRealIp() {
        return getRealIps().iterator().next();
    }

    /**
     *
     * @param args args
     */
    public static void main(String[] args) {
        System.out.println(getRealIp());
    }
}
